/*
* Copyright 2010 Competentum group
*
* Licensed under the GNU General Public License, Version 3.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.gnu.org/licenses/gpl.html
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.competentum.history
{
	import com.competentum.data.parser.XMLParser;
	import com.competentum.engines.InputOutputEngine;
	import com.competentum.events.HistoryEvent;
	import com.competentum.events.WorkloadEvent;
	import com.competentum.windows.ScopesManager;
	import com.competentum.lib.events.CommandEvent;
	
	import flash.events.Event;
	import flash.xml.XMLDocument;
	
	import mx.core.FlexGlobals;
	import mx.utils.ObjectUtil;
	
	/**
	 * Manages history entities
	 * 
	 * @author Valentine Abanosimov, OsipovS
	 */	
	public class HistoryChangesManager
	{
		/**
		 * Holds full states
		 * */
		private var historyStack:Vector.<XML> = new Vector.<XML>();
		
		/**
		 * Holds undo commands
		 * */
		private var undoStack:Vector.<HistoryItem> = new Vector.<HistoryItem>();
		/**
		 * Holds redo commands
		 * */
		private var redoStack:Vector.<HistoryItem> = new Vector.<HistoryItem>();
		
		private var cursor:int = -1;

		public function HistoryChangesManager()
		{
		}
		
		/**
		 * Adds command to history.
		 * One undo command can consist of a number of simple actions, so we use array
		 * */
		public function addCommands(undoEvents:Array, redoEvents:Array):void
		{
			var lastConfig:String = (historyStack.length > 0) ? String(historyStack[historyStack.length - 1]) : null;
			var newXML:XML = XMLParser.getDeserializeConfig((undoEvents[0] as WorkloadEvent).scope);
			var newConfig:String = String(newXML);
			if (lastConfig != newConfig)
			{
				clearAfterCurrentPosition();
				undoStack.push(new HistoryItem(undoEvents));
				redoStack.push(new HistoryItem(redoEvents));
				historyStack.push(XML(ObjectUtil.copy(newXML)));
				cursor ++ ;
			}
		}
		
		/**
		 * Dispatches events in command that causes engines to perform undo actions
		 * */
		public function undo():void
		{
			if (cursor < 0)
			{
				return ;
			}
			var historyItem:HistoryItem = undoStack[cursor] as HistoryItem;
			cursor -- ;
			for (var i:int = 0; i < historyItem.length; i ++ )
			{
				ScopesManager.currentWindow.dispatchEvent(historyItem.getEventAt(i));
			}
		}
		
		public function redo():void
		{
			if (cursor >= redoStack.length - 1)
			{
				return ;
			}
			var historyItem:HistoryItem = redoStack[cursor + 1] as HistoryItem;
			cursor ++ ;
			for (var i:int = 0; i < historyItem.length; i ++ )
			{
				ScopesManager.currentWindow.dispatchEvent(historyItem.getEventAt(i));
			}
		}
		
		private function clearFromPosition(position:int):void
		{
			if (position < undoStack.length)
			{
				undoStack.splice(position, undoStack.length-position);
				redoStack.splice(position, redoStack.length-position);
				historyStack.splice(position, historyStack.length-position);
				cursor = position - 1;
			}
		}
		
		private function clearAfterCurrentPosition():void
		{
			clearFromPosition(cursor + 1);
		}
		
		public function clearAll():void
		{
			clearFromPosition(0);
		}
		
		/**
		 * Groups some last commands into one. May be used in the case when a number of undo commands corresponds to one real command
		 * */
		public function groupLastCommands(count:int = 2):void
		{
			if (cursor >= undoStack.length - count + 1)
			{
				cursor = undoStack.length - count;
			}
			var undoArray:Array = [];
			var redoArray:Array = [];
			var i:int;
			for (i = 0; i < count; i ++ )
			{
				var undoItem:HistoryItem = undoStack[undoStack.length - count + i];
				for (var eventIndex:int = 0; eventIndex < undoItem.length; eventIndex ++ )
				{
					undoArray.push(undoItem.getEventAt(eventIndex));
				}
				var redoItem:HistoryItem = redoStack[redoStack.length - count + i];
				for (eventIndex = 0; eventIndex < redoItem.length; eventIndex ++ )
				{
					redoArray.push(redoItem.getEventAt(eventIndex));
				}
			}
			var xmlItem:XML = historyStack[historyStack.length - 1];
			for (i = 0; i < count; i ++ )
			{
				undoStack.pop();
				redoStack.pop();
				historyStack.pop();
			}
			undoStack.push(new HistoryItem(undoArray));
			redoStack.push(new HistoryItem(redoArray));
			historyStack.push(xmlItem);
		}
	}
}
